#include <iostream>
#include <memory>
#include <sys/select.h>
#include "Log.hpp"
#include "socket.hpp"

const int default_backlog = 5;
const static int num = sizeof(fd_set) * 8;
class SelectServer
{
private:
    void HanderAccept(const fd_set &set)
    {
        // 可能会处理多个文件描述符

        for (size_t i = 0; i < num; i++)
        {
            
            if (_rfds_array[i] == nullptr)
            {
                continue;
            }
            // 合法的套接字
            else
            {
                // 注意这里会出现：1.Accept新链接  2.read获取到新数据
                int fd = _rfds_array[i]->GetSockFd();
                // 判断fd是否在这个集合里
                if (FD_ISSET(fd, &set))
                {
                    // 表示 获取套接字就绪  
                    if (fd == _listen_sock->GetSockFd())
                    {
                        lg.LogMessage(Info, "get a new link\n");
                        std::string client_ip;
                        uint16_t client_port;
                        NetWork::Socket *sock = _listen_sock->AcceptConnection(&client_ip, &client_port);
                        if (sock == nullptr)
                        {
                            lg.LogMessage(Error, "sock accept failed\n");
                        }
                        lg.LogMessage(Info, "connect client success, client info is %s:%d, fd = %d\n", client_ip.c_str(), client_port, sock->GetSockFd());

                        // 建立好了链接，现在不确定读、写事件是否就绪
                        // 所以我们需要对读、写进行select
                        // 此时我们需要将新增的套接字，交由select进行托管
                        int pos = 0;

                        for (; pos < num; pos++)
                        {
                            if (_rfds_array[pos] == nullptr)
                            {
                                _rfds_array[pos] = sock;
                                break;
                            }
                        }
                        if (pos == num)
                        {
                            // 服务器select托管套接字已达上限
                            sock->CloseSockFd();
                            delete sock;
                            lg.LogMessage(Warning, "server is full...\n");
                        }
                    }
                    // 表示 读取数据 就绪
                    else
                    {
                        std::string buffer;
                        bool ret = _rfds_array[i]->Recv(&buffer, 1024);
                        if(ret == true)
                        {
                            std::cout<<"client message: "<<buffer<<std::endl;
                            buffer.clear();
                        }
                        else
                        {
                            // 差错处理 client文件描述符关闭 或者是 recv出现错误
                            lg.LogMessage(Error, "recv message failed\n");
                            _rfds_array[i]->CloseSockFd();
                            delete _rfds_array[i];
                            _rfds_array[i] = nullptr;
                        }
                    }
                }
            }
        }
    }

public:
    SelectServer(int port) : _port(port), _listen_sock(new NetWork::TcpSocket()), _isrunning(false) {}

    void InitServer()
    {
        _listen_sock->BuildListenSocketMethod(_port, default_backlog);
        // 初始化话fd数组
        for (size_t i = 0; i < num; i++)
        {
            _rfds_array[i] = nullptr;
        }

        _rfds_array[0] = _listen_sock.get();    // 获取到内部的指针
    }

    void Loop()
    {
        _isrunning = true;
        while (_isrunning == true)
        {
            // accept本质上是一个读事件，所以我们不能先进行accept
            // 而是先进行select监听所有的文件描述符，进行等待
            fd_set rfds;
            FD_ZERO(&rfds);
            // 获取最大的fd然后作为select的参数
            int max_fd = _listen_sock->GetSockFd();
            for (size_t i = 0; i < num; i++)
            {
                if (_rfds_array[i] == nullptr)
                {
                    continue;
                }
                else
                {
                    // 当前文件描述符fd
                    int fd = _rfds_array[i]->GetSockFd();
                    // 需要将文件描述符添加到这个集合中，将新的套接字添加到rfds中
                    FD_SET(fd, &rfds);

                    if (max_fd < fd)
                    {
                        max_fd = fd;
                    }
                }
            }

            struct timeval timeout = {5, 0};
            int n = select(max_fd + 1, &rfds, nullptr, nullptr, &timeout);

            if (n > 0) // select成功
            {
                lg.LogMessage(Info, "select success, last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
                // 这里对响应的Accept进行处理
                HanderAccept(rfds);
            }
            else if (n == 0) // select超时
            {
                lg.LogMessage(Info, "select timeout, last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
            }
            else // select失败
            {
                lg.LogMessage(Error, "select error\n");
            }
        }
        _isrunning = false;
    }

private:
    std::unique_ptr<NetWork::Socket> _listen_sock;
    int _port;
    bool _isrunning;

    // select服务器需要通过数据结构维护文件描述符
    NetWork::Socket *_rfds_array[num];
};